from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import sys, math, string
from math import *
from time import *

import Image
from Image import *
from opencv.cv import *
from opencv.highgui import *
from opencv.adaptors import *

from mosaic import *
from shader import SimpleShader
from urllib import *
from flickr import *
from cStringIO import StringIO

# Some api in the chain is translating the keystrokes to this octal string
# so instead of saying: ESCAPE = 27, we use the following.
ESCAPE = '\033'

W = 1024
H = 768
mapsize = (1024,1024)
big_imsize = (1024,1024)
tiles = (64.,48.)
cpage = 1;
p_ind = 0;

cam = cvCreateCameraCapture(0)
mapim = Image.new('RGBA', mapsize, (0, 0, 0, 0))

texs = []
toggle = True
BW = False
CIRCLES=True
POLYGON=False
UPDATE = False
SEARCH = False
SIDES = 4
SAVEIM = None
photos = None

mosaic = Mosaic("mosaicmap.jpg", mapsize, indexed=False, useAvg=False, bw=BW)
mosaiced = None
frame = None

shade = SimpleShader("/Users/ollie/Documents/workspace/videomosey/src/shaders/tex")

def makeTexture():
#    LoadVideo()
    glPixelStorei(GL_UNPACK_ALIGNMENT,1)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
    
def LoadTextures():
#    global ix, iy, image
    global mapsize, texs, imsize, mapim
    # Create Texture
    texs = glGenTextures(2)    
    glBindTexture(GL_TEXTURE_2D, texs[0])   # 2d texture (x and y size)
    glTexImage2D(GL_TEXTURE_2D, 0, 3, mapsize[0], mapsize[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, mapim.tostring("raw", "RGBA", 0, -1))
    makeTexture()

    glBindTexture(GL_TEXTURE_2D, texs[1])   # 2d texture (x and y size)
    glTexImage2D(GL_TEXTURE_2D, 0, 3, big_imsize[0], big_imsize[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, None)

    makeTexture()

# A general OpenGL initialization function.  Sets all of the initial parameters. 
def InitGL():                # We call this right after our OpenGL window is created.
    shade.loadShaders() 
    shade.setUniform("map", [0])
    shade.setUniform("frame", [1])
    shade.setUniform("npix", [tiles[0], tiles[1]])
    LoadTextures()
    glEnable(GL_TEXTURE_2D)
    glClearColor(0.0, 0.0, 0.0, 0.0)    # This Will Clear The Background Color To Black
    glClearDepth(1.0)                    # Enables Clearing Of The Depth Buffer
    glDepthFunc(GL_LESS)                # The Type Of Depth Test To Do
    glEnable(GL_DEPTH_TEST)                # Enables Depth Testing
    glShadeModel(GL_SMOOTH)                # Enables Smooth Color Shading
    
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()                    # Reset The Projection Matrix
    gluOrtho2D(-1, 1, -1, 1)
    glMatrixMode(GL_MODELVIEW)
    

# The main drawing function. 
def updateTextures():
    global mosiac, mosaiced, frame

    frame = Ipl2PIL(cvQueryFrame(cam))

#    if frame == None:
#        os.chdir("/Users/ollie/Documents/workspace/videomosey/src/snapshots/")
#        frame = Image.open("tim.bmp")

    if mosaic.bw:
        frame = frame.convert('L').convert('RGB')
    if UPDATE:    
        mosaic.addImage(frame)

    if toggle:
        shade.setUniform("viewMap", [1])
        glActiveTexture(GL_TEXTURE0_ARB)
        glBindTexture(GL_TEXTURE_2D, texs[0])   # 2d texture (x and y size)
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mapsize[0], mapsize[1], GL_RGBA, GL_UNSIGNED_BYTE, mosaic.mapim.tostring("raw", "RGBA", 0, -1))
    else:
        shade.setUniform("viewMap", [0])
        glActiveTexture(GL_TEXTURE0_ARB + 1)
        mosaiced = frame.resize(tiles).resize(big_imsize).convert("RGBA")   
        #mosaiced = Image.open("/Users/ollie/Documents/workspace/videomosey/src/circles.jpg").resize(tiles).resize(big_imsize).convert("RGBA")   
        glBindTexture(GL_TEXTURE_2D, texs[1])   # 2d texture (x and y size)
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, big_imsize[0], big_imsize[1], GL_RGBA, GL_UNSIGNED_BYTE, mosaiced.tostring("raw", "RGBA", 0, -1))

def DrawGLScene():
    global SAVEIM
    updateTextures()
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)    # Clear The Screen And The Depth Buffer

    h = min(W, H)
    w = int(4./3.*h) 
    w1 = W/2-w/2
    h1 = H/2-h/2
    glViewport(w1, h1, w, h)        # Reset The Current Viewport And Perspective Transformation

    shade.bind()
    glBegin(GL_QUADS)                # Start Drawing The Cube
    
    # Front Face (note that the texture's corners have to match the quad's corners)
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0)    # Bottom Left Of The Texture and Quad
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0)    # Bottom Right Of The Texture and Quad
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0)    # Top Right Of The Texture and Quad
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0)    # Top Left Of The Texture and Quad
    glEnd()

    if SAVEIM:
        import os
        os.chdir("/Users/ollie/Documents/workspace/videomosey/src/snapshots/")
        name = strftime("%Y_%m%d_%H_%M_%S") + ".bmp"
        #NumPy2PIL(glReadPixelsub(0, 0, W, H, GL_RGB)).save(name)
        data = glReadPixelsub(0, 0, W, H, GL_RGB)
        image = Image.fromstring( "RGB", (W, H), data.tostring() )
        image = image.transpose( Image.FLIP_TOP_BOTTOM)
        image.save( name)   
        SAVEIM = None
        
    #  since this is double buffered, swap the buffers to display what just got drawn. 
    glutSwapBuffers()
        
def keyboard(*args):
    print "key pressed"
    global toggle, BW, CIRCLES, POLYGON, UPDATE, SEARCH, SIDES, SAVEIM
    # If escape is pressed, kill everything.
    key = string.upper(args[0])
    if key == 'B':        
        BW = not BW
        shade.setUniform("BW", [int(BW)])
        if BW:
            mosaic.setBW(True)
        else:
            mosaic.setBW(False)
    if key == 'C':
        CIRCLES = not CIRCLES
        if not CIRCLES:
            mosaic.setShape(MOSAIC_SHAPE_SQUARES)
        else:
            mosaic.setShape(MOSAIC_SHAPE_CIRCLES)
    if key == 'P':
        POLYGON = not POLYGON
        if not POLYGON:
            mosaic.setShape(MOSAIC_SHAPE_SQUARES)
        else:
            mosaic.setShape(MOSAIC_SHAPE_POLYGON, SIDES)
    if key == '+':
        SIDES = SIDES + 1
        if SIDES > 8:
            SIDES = 8;
        if POLYGON:
            mosaic.setShape(MOSAIC_SHAPE_POLYGON, SIDES)
    if key == '-':
        SIDES = SIDES - 1
        if SIDES < 3:
            SIDES = 3;
        if POLYGON:
            mosaic.setShape(MOSAIC_SHAPE_POLYGON, SIDES)
    if key == 'Q':
        toggle = not toggle
    if key == 'U':
        UPDATE = not UPDATE
    if key == 'W':
        SEARCH = not SEARCH
    if key == 'F':
        glutFullScreen()
    if key == 'S':
        SAVEIM = not SAVEIM
#        import os
#        os.chdir("/Users/ollie/Documents/workspace/videomosey/src/snapshots/")
#        name = strftime("%Y_%m%d_%H_%M_%S") + ".bmp"
#        im.save(name)
    elif key == ESCAPE:
        sys.exit()

    glActiveTexture(GL_TEXTURE0_ARB)
    glBindTexture(GL_TEXTURE_2D, texs[0])   # 2d texture (x and y size)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mapsize[0], mapsize[1], GL_RGBA, GL_UNSIGNED_BYTE, mosaic.mapim.tostring("raw", "RGBA", 0, -1))
    glutPostRedisplay()

# The function called when our window is resized (which shouldn't happen if you enable fullscreen, below)
def ReSizeGLScene(Width, Height):
    global W, H
    if Height == 0:                        # Prevent A Divide By Zero If The Window Is Too Small 
        Height = 1

    W = Width
    H = Height

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(-1, 1, -1, 1)
    glMatrixMode(GL_MODELVIEW)

def idleFunc():
    global p_ind, cpage, photos
    if SEARCH:
        if p_ind == 0:    
            photos = photos_search(text="christmas ornament ball", page=cpage, sort="relevance", per_page=500)
    
        url = photos[p_ind].getURL( size = "Square", urlType = 'source')
        im = Image.open(StringIO(urlopen(url).read()))
        if mosaic.bw:
            im = im.convert('L').convert('RGB')
        mosaic.addImage(im)    

        img = Image.new('RGB', (128,128))
        img.paste(im.resize((112,112), Image.BICUBIC), (8,8,120,120))
        mosaic.addImage(img)    
        img = Image.new('RGB', (128,128))
        img.paste(im.resize((96,96), Image.BICUBIC), (16,16,112,112))
        mosaic.addImage(img)    
        img = Image.new('RGB', (128,128))
        img.paste(im.resize((80,80), Image.BICUBIC), (24,24,104,104))
        mosaic.addImage(img)    
        img = Image.new('RGB', (128,128))
        img.paste(im.resize((64,64), Image.BICUBIC), (32,32,96,96))
        mosaic.addImage(img)    
        img = Image.new('RGB', (128,128))
        img.paste(im.resize((48,48), Image.BICUBIC), (40,40,88,88))
        mosaic.addImage(img)    
        img = Image.new('RGB', (128,128))
        img.paste(im.resize((32,32), Image.BICUBIC), (48,48,80,80))
        mosaic.addImage(img)    


        p_ind = p_ind + 1
    
        if p_ind >= 500:
            cpage = cpage + 1
            p_ind = 0
    glutPostRedisplay()

def main():
    global window, big_imsize, window_size
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
    glutInitWindowSize(1024, 768)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("VideoMosey")
    glutDisplayFunc(DrawGLScene)
    glutIdleFunc(idleFunc)
    glutReshapeFunc(ReSizeGLScene)
    glutKeyboardFunc(keyboard)
    InitGL()
    glutMainLoop()

# Print message to console, and kick off the main to get it rolling.
print "Hit ESC key to quit."
main()
        
